/*
 * MIT License
 *
 * Copyright (c) 2024-2048 冰羽
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package cn.star.framework.util;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import cn.star.framework.core.dict.EnumEntry;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 处理继承{@link EnumEntry}接口的枚举<br>
 *
 * @author zhaoweiping
 *     <p style='color: red'>Created on 2024-09-23 11:06:00
 * @since 3.0.0
 */
public class EnumEntryHelper {

  @SuppressWarnings("unchecked")
  public static EnumEntryHelper get(Class<?> clazz) {
    if (!clazz.isEnum()) {
      throw new RuntimeException(
          "Not support type: " + clazz.getSimpleName() + ", Only supports enum");
    }
    if (!EnumEntry.class.isAssignableFrom(clazz)) {
      throw new RuntimeException(
          EnumEntry.class.getSimpleName()
              + " is not a subclass of "
              + clazz.getSimpleName()
              + ", Not support");
    }

    return new EnumEntryHelper((Class<? extends EnumEntry<? extends Serializable>>) clazz);
  }

  private final Class<? extends EnumEntry<? extends Serializable>> clazz;

  private EnumEntryHelper(Class<? extends EnumEntry<? extends Serializable>> clazz) {
    this.clazz = clazz;
  }

  public List<EnumEntry<?>> getValues() {
    EnumEntry<?>[] instances = clazz.getEnumConstants();

    return instances == null || instances.length == 0
        ? new ArrayList<>()
        : CollectionUtil.toList(instances);
  }

  /**
   * 根据标签或值查询枚举值
   *
   * @param vl {@link EnumEntry#getValue()}
   * @param <Vl>
   * @return
   */
  public <Vl extends Serializable, E extends EnumEntry<Vl>> E getValue(Vl vl) {
    return this.getValue(vl, true, null);
  }

  /**
   * 根据标签或值查询枚举值
   *
   * @param vl {@link EnumEntry#getValue()}/{@link EnumEntry#getLabel()}
   * @param isValue
   * @param <Vl>
   * @return
   */
  public <Vl extends Serializable, E extends EnumEntry<Vl>> E getValue(Vl vl, boolean isValue) {
    return this.getValue(vl, isValue, null);
  }

  /**
   * 根据标签或值查询枚举值
   *
   * @param vl {@link EnumEntry#getValue()}/{@link EnumEntry#getLabel()}
   * @param isValue 是否值
   * @param defaultValue 默认值
   * @return 值
   */
  @SuppressWarnings({"unchecked", "rawtypes"})
  public <Vl extends Serializable, R extends EnumEntry<Vl>> R getValue(
      Vl vl, boolean isValue, R defaultValue) {
    EnumEntry<Vl> entry = null;
    List<EnumEntry<?>> values = this.getValues();

    if (vl != null) {
      for (EnumEntry v : values) {
        entry = vl.equals(isValue ? v.getValue() : v.getLabel()) ? v : null;
        if (entry != null) {
          break;
        }
      }
    } else {
      entry = defaultValue;
    }

    if (entry == null) {
      throw new RuntimeException(
          StrUtil.format(
              "Not support type: {}, support types: {}",
              vl,
              values.stream()
                  .map(item -> StrUtil.format("{}({})", item.getLabel(), item.getValue()))
                  .collect(Collectors.joining(", "))));
    }

    return (R) entry;
  }
}
